package net.zigin.epi.demo.aop;

import com.alibaba.fastjson.serializer.PropertyFilter;
import com.alibaba.fastjson.serializer.SerializeFilter;
import net.zigin.epi.demo.annotation.EpiResponseFilter;
import net.zigin.epi.demo.annotation.EpiResponseFilters;
import net.zigin.epi.demo.converter.EpiFastJsonHttpMessageConverter;
import net.zigin.epi.demo.converter.EpiResponseFilterAdapter;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 返回值拦截器
 *
 * @author mgw1986
 * @version V1.0
 * @date 2020-08-11 23:35
 */
@Order(2)
@ControllerAdvice
public class EpiResponseAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return converterType.isAssignableFrom(EpiFastJsonHttpMessageConverter.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        EpiResponseFilterAdapter responseFilterAdapter = createResponseFilterAdapter(body);
        List<SerializeFilter> filters = new ArrayList<>();

        for (Annotation annotation : returnType.getMethodAnnotations()) {
            if (annotation instanceof EpiResponseFilter) {
                final EpiResponseFilter propertyFilter = (EpiResponseFilter) annotation;
                final Class<?> target = propertyFilter.target();
                filters.add(creatFilter(propertyFilter, target));
            } else if (annotation instanceof EpiResponseFilters) {
                EpiResponseFilters propertyFilters = (EpiResponseFilters) annotation;
                for (EpiResponseFilter propertyFilter : propertyFilters.value()) {
                    Class<?> target = propertyFilter.target();
                    filters.add(creatFilter(propertyFilter, target));
                }
            }
        }
        responseFilterAdapter.setFilters(filters.toArray(new SerializeFilter[0]));
        return responseFilterAdapter;
    }

    private SerializeFilter creatFilter(EpiResponseFilter propertyFilter, Class<?> target) {
        return new PropertyFilter() {
            @Override
            public boolean apply(Object object, String name, Object value) {
                if (target != Object.class) {
                    if (!object.getClass().isAssignableFrom(target)) {
                        return true;
                    } else {
                        return _apply(name);
                    }
                } else {
                    return _apply(name);
                }
            }

            private boolean _apply(String name) {
                //两者同时存在的情况下,无法同时判断,因为还有其他属性.
                //优先判断exclues,其次是includes
                String[] includes = propertyFilter.include().split(",");
                String[] excludes = propertyFilter.exclude().split(",");
                if (includes.length > 0 && excludes.length > 0) {
                    //两者都存在,只处理排除属性
                    return !(Arrays.binarySearch(excludes, name) > -1);
                } else if (excludes.length > 0) {
                    //只排除某些属性
                    return !(Arrays.binarySearch(excludes, name) > -1);
                } else if (includes.length > 0) {
                    //只包含某些属性
                    return Arrays.binarySearch(includes, name) > -1;
                }
                //两者都不存在
                return true;
            }
        };
    }


    private EpiResponseFilterAdapter createResponseFilterAdapter(Object body) {
        return body instanceof EpiResponseFilterAdapter ? (EpiResponseFilterAdapter) body : new EpiResponseFilterAdapter(body);
    }
}
